package com.gc.logger

import android.content.Context
import android.util.Log
import com.gc.logger.entity.LogEntity
import com.gc.logger.interfaces.ILogPrinter
import com.gc.logger.interfaces.ILogStrategy
import com.gc.logger.printer.AndroidLogPrinter
import com.gc.logger.strategy.DefLogStrategy
import java.io.File
import java.util.concurrent.Executor
import java.util.concurrent.Executors

/**
 * 日志工具类
 */
class LogUtils {
    private lateinit var mContext: Context
    private var defTag: String = ""
    private var executor: Executor = Executors.newSingleThreadExecutor()
    private val logPrinters: ArrayList<ILogPrinter> = arrayListOf()
    private val logStrategys: MutableMap<String, ILogStrategy> = mutableMapOf()
    private var isDebug = true

    companion object {
        @JvmStatic
        val INSTANCE by lazy { LogUtils() }
        const val MAX_LOG_LENGTH = 4000
        const val MAX_BYTES = 500 * 1024
        const val LOG_ALL = "LOG_ALL"
        const val LOG_CONTENT_FORMAT = "[%s][%s][%s]%s\n"
        val defFilePath: String
            get() = INSTANCE.mContext.getExternalFilesDir("log").toString()
        @JvmStatic
        @JvmOverloads
        fun e(tag: String?, message: String, tr: Throwable? = null, logStrategy: String? = null) {

            INSTANCE.printLog(LogEntity(tag ?: "",
                message,
                tr,
                Log.ERROR,
                INSTANCE.defTag,
                logStrategy))
        }

        @JvmStatic
        @JvmOverloads
        fun w(tag: String?, message: String, tr: Throwable? = null, logStrategy: String? = null) {
            INSTANCE.printLog(LogEntity(tag ?: "",
                message,
                tr,
                Log.WARN,
                INSTANCE.defTag,
                logStrategy))
        }

        @JvmStatic
        @JvmOverloads
        fun d(tag: String?, message: String, tr: Throwable? = null, logStrategy: String? = null) {
            INSTANCE.printLog(LogEntity(tag ?: "",
                message,
                tr,
                Log.DEBUG,
                INSTANCE.defTag,
                logStrategy))
        }

        @JvmStatic
        @JvmOverloads
        fun i(tag: String?, message: String, tr: Throwable? = null, logStrategy: String? = null) {
            INSTANCE.printLog(LogEntity(tag ?: "",
                message,
                tr,
                Log.INFO,
                INSTANCE.defTag,
                logStrategy))
        }

        @JvmStatic
        @JvmOverloads
        fun v(tag: String?, message: String, tr: Throwable? = null, logStrategy: String? = null) {
            INSTANCE.printLog(LogEntity(tag ?: "",
                message,
                tr,
                Log.VERBOSE,
                INSTANCE.defTag,
                logStrategy))
        }
    }
    @JvmOverloads
    fun init(context: Context, defTag: String = "", isDebug: Boolean = true): LogUtils {
        if(logPrinters.isNotEmpty()){
            throw RuntimeException("logger is inited")
        }
        this.mContext = context
        this.isDebug = isDebug
        this.defTag = defTag
        if (this.defTag.isEmpty()) {
            this.defTag = mContext.packageName
        }
        logPrinters.add(AndroidLogPrinter())
        logStrategys[LOG_ALL] = DefLogStrategy()
        return this
    }

    fun addPrinter(iLogPrinter: ILogPrinter): LogUtils {
        synchronized(this) {
            logPrinters.add(iLogPrinter)
        }
        return this
    }

    fun addStrategy(iLogStrategy: ILogStrategy): LogUtils {
        synchronized(this) {
            logStrategys[iLogStrategy.logStrategy] = iLogStrategy
        }
        return this
    }

    fun clearAllPrinter(): LogUtils {
        synchronized(this) {
            logPrinters.clear()
        }
        return this
    }

    fun clearAllStrategy(): LogUtils {
        synchronized(this) {
            logStrategys.clear()
        }
        return this
    }

    /**
     * 清除log缓存
     */
    fun clearCache(filePath: String = defFilePath, day: Int = 7) {
        try {
            val cacheFile = File(filePath)
            if (cacheFile.exists()) {
                val files: Array<File> = cacheFile.listFiles()
                for (file in files) {
                    delTimeOutLogFile(day, file)
                }
            }
        } catch (e: Exception) {

        }
    }

    private fun delTimeOutLogFile(day: Int, file: File) {
        try {
            var curtime: Long = System.currentTimeMillis()
            var timeout: Long = day * 24 * 3600 * 1000L
            var timeOutTime: Long = curtime - timeout
            if (file.lastModified() < timeOutTime) {
                file.delete()
            }
        } catch (e: Exception) {

        }
    }

    enum class LogType {
        System, File, Upload
    }

    private fun printLog(logger: LogEntity) {
        if(logPrinters.isEmpty()){
            throw RuntimeException("logger is not init")
        }
        executor.execute {
            logger.tr?.let {
                logger.message = "${logger.message}\n${Log.getStackTraceString(it)}"
            }
            logPrinters.filter {
                checkStrategyPrint(it,
                    logger,
                    logStrategys[logger.logStrategy ?: logger.tag]) || checkStrategyPrint(it,
                    logger,
                    logStrategys[LOG_ALL])
            }.forEach {
                it.log(mContext, logger)
            }
//            logPrinters.forEach {
//                (strategyPrint(it,logger,logStrategys[logger.tag])?:strategyPrint(it,logger,logStrategys[LOG_ALL]))?.log(logger)
//            }
        }
    }

    //    private fun strategyPrint(logPrinter: ILogPrinter, logger: LogEntity, iLogStrategy: ILogStrategy?):ILogPrinter? {
//        return  iLogStrategy?.let {
//                if (it.isCanPrint(isDebug,logger.priority,logPrinter.logType))logPrinter else null
//        }?:null
//    }
    private fun checkStrategyPrint(
        logPrinter: ILogPrinter,
        logger: LogEntity,
        iLogStrategy: ILogStrategy?,
    ): Boolean {
        return iLogStrategy?.let {
            it.isCanPrint(isDebug, logger.priority, logPrinter.logType, logger.tag)
        } ?: false
    }
}